﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading.Tasks;
using TransformerManager.Shared.Dto;
using TransformerManager.Shared.Helper;
using TransformerManager.EF.Models;
using TransformerManager.Shared.Servers;
using TransformerManager.API.Utils;
using TransformerManager.Shared.Helper.HttpParams;

namespace TransformerManager.API.Controllers
{
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    [Authorize]
    public class AuthenticateController : ControllerBase
    {
        private readonly IConfiguration _configuration;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;
        public AuthenticateController(
            IConfiguration configuration,
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager)
        {
            this._configuration = configuration;
            this._userManager = userManager;
            this._signInManager = signInManager;

        }

        private string GenerateRandomNumber(int len = 32)
        {
            var randomNumber = new byte[len];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

        /// <summary>
        /// 生成JwtToken
        /// </summary>
        /// <param name="user">用户对象</param>
        /// <param name="roleNames">用户角色列表</param>
        /// <returns></returns>
        private TokenParams GenerateJwtToken(ApplicationUser user, IList<string> roleNames)
        {
            var jwtSetting = new JwtSetting(_configuration);

            foreach (var roleName in roleNames)
            {
                // 在claims中添加不同角色名，具有不同的访问权限
                jwtSetting.Subject.AddClaim(new Claim(ClaimTypes.Role, roleName));
            }
            jwtSetting.Subject.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
            var tokenDescr = new SecurityTokenDescriptor()
            {
                Audience = jwtSetting.Audience,
                Issuer = jwtSetting.Issuer,
                NotBefore = DateTime.UtcNow,
                Expires = jwtSetting.Expires,
                Subject = jwtSetting.Subject,
                SigningCredentials = jwtSetting.SigningCredentials
            };

            var securityToken = new JwtSecurityTokenHandler().CreateToken(tokenDescr);

            var jwtTokenStr = new JwtSecurityTokenHandler().WriteToken(securityToken);

            var refreshToken = new RefreshToken()
            {
                JwtId = securityToken.Id,
                UserId = user.Id,
                CreationTime = DateTime.UtcNow,
                ExpiryTime = DateTime.UtcNow.AddDays(1),
                Token = GenerateRandomNumber()
            };

            //_userService.AddRefreshToken(refreshToken);
            //await _userService.SaveChangesAsync();

            return new TokenParams()
            {
                AccessToken = jwtTokenStr,
                TokenType = "Bearer",
                RefreshToken = refreshToken.Token,
                ExpiresIn = jwtSetting.Expires.Second
            };
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="loginDto">登录参数</param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("login")]
        public async Task<ActionResult<ServiceResponse<TokenParams>>> Login([FromBody] LoginDto loginDto)
        {
            var loginResult = await _signInManager.PasswordSignInAsync(
                loginDto.UserName,
                loginDto.Password,
                false,
                false);

            var response = new ServiceResponse<TokenParams>();

            if (!loginResult.Succeeded)
            {
                response.Success = false;
                response.Message = "账户或密码错误!";
                return NotFound(response);
            }

            // payload
            var user = await _userManager.FindByNameAsync(loginDto.UserName);
            // 获取用户角色
            var roleNames = await _userManager.GetRolesAsync(user);

            var tokenResult = GenerateJwtToken(user, roleNames);

            response.Data = tokenResult;

            return Ok(response);
        }

        /// <summary>
        /// 注册
        /// </summary>
        /// <param name="registerDto">注册参数</param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("register")]
        public async Task<ActionResult<ServiceResponse>> Register([FromBody] RegisterDto registerDto)
        {
            // 使用用户名创建账户
            var user = new ApplicationUser()
            {
                UserName = registerDto.UserName,
                RealNameEn = registerDto.RealNameEn,
                RealNameCn = registerDto.RealNameCn,
                Department = registerDto.Department,
                IsLeadEngineer = registerDto.IsLeadEngineer
            };

            var result = await _userManager.CreateAsync(user, registerDto.Password);

            var response = new ServiceResponse();

            if (!result.Succeeded)
            {
                response.Success = false;
                response.Message = "用户注册失败!";
                return BadRequest(response);
            }

            // 创建并保存成功
            response.Message = "用户注册成功!";
            return Ok(response);
        }

        /// <summary>
        /// 登出
        /// </summary>
        /// <returns></returns>
        [Authorize(AuthenticationSchemes = "Bearer")]
        [HttpGet("logout")]
        public async Task<ActionResult<ServiceResponse>> Logout()
        {
            await _signInManager.SignOutAsync();
            var response = new ServiceResponse() { Message = "登出成功!" };
            return Ok(response);
        }
    }
}
